/*
 * License GNU LGPL
 * Copyright (C) 2012 Amrullah <amrullah@panemu.com>.
 */
package com.abc.cheque.ui.table;


import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;

import com.abc.cheque.common.TiwulFXUtil;
import com.abc.cheque.common.TableCriteria.Operator;
import com.abc.cheque.control.DateField;

/**
 *
 * @author amrullah
 */
public class DateColumn<S> extends BaseColumn<S, Date> {

    private DateField searchInputControl = new DateField();
    private SearchMenuItemBase<Date> searchMenuItem = new SearchMenuItemBase<Date>(this) {
        {
            searchInputControl.setShowTodayButton(true);
        }

        @Override
        protected Node getInputControl() {
            return searchInputControl;
        }

        @Override
        protected List<Operator> getOperators() {
            List<Operator> lst = new ArrayList<>();
            lst.add(Operator.eq);
            lst.add(Operator.ne);
            lst.add(Operator.lt);
            lst.add(Operator.le);
            lst.add(Operator.gt);
            lst.add(Operator.ge);
            lst.add(Operator.is_null);
            lst.add(Operator.is_not_null);
            return lst;
        }

        @Override
        protected Date getValue() {
            return searchInputControl.getSelectedDate();
        }
    };

    public DateColumn(String propertyName) {
        this(propertyName, 100);
    }

    public DateColumn(String propertyName, double preferredWidth) {
        super(propertyName, preferredWidth);
        Callback<TableColumn<S, Date>, TableCell<S, Date>> cellFactory =
                new Callback<TableColumn<S, Date>, TableCell<S, Date>>() {
                    @Override
                    public TableCell call(TableColumn p) {
                        return new DateTableCell();
                    }
                };
        setCellFactory(cellFactory);
    }

    @Override
    public MenuItem getSearchMenuItem() {
        searchInputControl.setSelectedDate(getDefaultSearchValue());
        return searchMenuItem;
    }

    @Override
    protected Date convertFromString_Impl(String stringValue) {
        try {
            Date parsedDate = dateFormat.get().parse(stringValue);
            parsedDate = dateFormat.get().parse(dateFormat.get().format(parsedDate));
            return parsedDate;
        } catch (ParseException ex) {
            return null;
        }
    }

    @Override
    public String convertToString(Date value) {
        if (value == null) {
            return null;
        }
        return dateFormat.get().format(value);
    }

    /**
     * Gets the date format.
     *
     * @return The date format.
     */
    public ObjectProperty<DateFormat> dateFormatProperty() {
        return dateFormat;
    }
    private ObjectProperty<DateFormat> dateFormat = new SimpleObjectProperty<DateFormat>(DateFormat.getDateInstance(DateFormat.SHORT, TiwulFXUtil.getLocale()));

    public void setDateFormat(DateFormat dateFormat) {
        this.dateFormat.set(dateFormat);
    }

    public DateFormat getDateFormat() {
        return dateFormat.get();
    }
    private ObjectProperty<Locale> locale = new SimpleObjectProperty<>(TiwulFXUtil.getLocale());

    public ObjectProperty<Locale> localeProperty() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale.set(locale);
    }

    public Locale getLocale() {
        return locale.get();
    }

    public class DateTableCell<S> extends BaseCell<S, Date> {

        private DateField datePicker;

        public DateTableCell() {
            super();
            this.setAlignment(DateColumn.this.getAlignment());
        }

        @Override
        protected void setValueToEditor(Date value) {
            datePicker.setSelectedDate(value);
        }

        @Override
        protected Date getValueFromEditor() {
            return datePicker.getSelectedDate();
        }

        @Override
        protected Control getEditor() {
            if (datePicker == null) {
                datePicker = new DateField(localeProperty().get());
                /**
                 * Disable traversing focus using LEFT and RIGHT.
                 */
                datePicker.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                    @Override
                    public void handle(KeyEvent event) {
                        if ((event.getCode() == KeyCode.LEFT || event.getCode() == KeyCode.RIGHT)
                                && isEditing()) {
                            event.consume();
                        }
                    }
                });
            }
            return datePicker;
        }

        @Override
        protected String getString(Date value) {
            return getItem() == null ? "" : dateFormat.get().format(getItem());
        }

        @Override
        protected void attachEnterEscapeEventHandler() {
            /**
             * Use event filter instead on onKeyPressed because Enter and Escape
             * have been consumed by Combobox it self
             */
            datePicker.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
                @Override
                public void handle(KeyEvent t) {
                    if (t.getCode() == KeyCode.ENTER) {
                        commitEdit(datePicker.getSelectedDate());
                        t.consume();
                    } else if (t.getCode() == KeyCode.ESCAPE) {
                        /**
                         * Call datePicker.getSelectedDate() to trigger
                         * validation in datePicker. It is needed in case user
                         * has change textfield's text. The cancelEdit() method
                         * will reassign selected date value with the original
                         * value. Since at that moment the selectedDate has
                         * changed due to validation, reassigning with original
                         * value will fire selectedDateProperty change thus the
                         * textfield's text is recomputed
                         */
                        datePicker.getSelectedDate();
                        cancelEdit();
                        /**
                         * Propagate ESCAPE key press to cell
                         */
                        DateTableCell.this.fireEvent(t);
                    }
                }
            });
        }
    }
}
